#include "GeneradorHashingPerfecto.h"

void GeneradorHashingPerfecto::inicializar() {
		funcionUno = new std::vector<unsigned>();
		funcionDos = new std::vector<unsigned>();
		grafo = NULL;
		generadorRandom = NULL;

		bool existeGrafoSerializado = false;
		bool esAciclico = false;
		bool grafoEtiquetadoCorrectamente = false;

		if (hidratarGrafo) {
			existeGrafoSerializado = hidratar();
		}

		if (!existeGrafoSerializado) {
			numeroDeAristas = palabras.size();
			numeroDeVertices = numeroDeAristas + CTE_ARISTAS*numeroDeAristas;
			generadorRandom =  new GeneradorNumeroRandom(numeroDeVertices);
		}

		while ((!esAciclico || !grafoEtiquetadoCorrectamente) && !existeGrafoSerializado ) {
			configurarFunciones();
			esAciclico = generarGrafo();

			if (!esAciclico) {
				numeroDeVertices = numeroDeVertices + CTE_ARISTAS*numeroDeAristas;

			}
			else {
				grafo->etiquetar();
				grafoEtiquetadoCorrectamente = verificarGrafo();
			}

		}
		if (!existeGrafoSerializado) {
			serializar();
		}
}

GeneradorHashingPerfecto::GeneradorHashingPerfecto(std::string indice) {
	this->indice=indice;
	hidratarGrafo = true;
	inicializar();
}

GeneradorHashingPerfecto::GeneradorHashingPerfecto() {
	hidratarGrafo = true;
	inicializar();
}
GeneradorHashingPerfecto::GeneradorHashingPerfecto(const std::string indice,std::vector <std::string*> & _palabras, unsigned longitudFuncionesConfigurables) {
	this->indice=indice;
	sizeFuncionesConfigurables = longitudFuncionesConfigurables;
	hidratarGrafo = false;
	palabras = (_palabras);
	inicializar();
}

bool GeneradorHashingPerfecto::verificarGrafo() {
	unsigned valor = 0;
	for (unsigned index = 1; index < numeroDeAristas; ++index) {
		valor = this->obtenerIndex(*palabras.at(index));
		if (valor != index) {
			return false;
		}
	}
	return true;
}

void GeneradorHashingPerfecto::imprimirEspacioHasheado() {
	for (unsigned i = 0; i < numeroDeAristas; ++i) {
		std::cout << "INDEX: " << i << " " << obtenerIndex(*palabras.at(i)) << std::endl;
	}
}

GeneradorHashingPerfecto::~GeneradorHashingPerfecto() {
	if (generadorRandom)
		delete(generadorRandom);

	delete(funcionUno);
	delete(funcionDos);

	if (grafo)
		delete(grafo);
}

void GeneradorHashingPerfecto::serializar() {
		std::stringstream path;
		std::string ruta="../archivos/salida/hash-"+this->indice+".bin";
		path << ruta ;

		std::string pathString = path.str();

		std::ofstream *salida = new std::ofstream(pathString.c_str(), std::ios::out | std::ios::binary);
		unsigned tamanioHeader = NUMERO_DE_FUNCIONES * sizeFuncionesConfigurables * sizeof (unsigned) + 3*sizeof (unsigned);
		unsigned tamanioInfoTabla = numeroDeVertices*sizeof(unsigned);
		char *header = new char [tamanioHeader];
		char *funcionG = new char [tamanioInfoTabla];
		char *ptr = header;
		unsigned maxLongitud = sizeFuncionesConfigurables;
		unsigned valorG = 0;

		memcpy(ptr, &numeroDeVertices, sizeof (unsigned));
		ptr += sizeof (unsigned);
		memcpy(ptr, &numeroDeAristas, sizeof (unsigned));
		ptr += sizeof (unsigned);
		memcpy(ptr, &maxLongitud, sizeof (unsigned));
		ptr += sizeof (unsigned);
		memcpy(ptr, funcionUno->data(), sizeof (unsigned)* sizeFuncionesConfigurables);
		ptr += sizeof (unsigned)* sizeFuncionesConfigurables;
		memcpy(ptr, funcionDos->data(), sizeof (unsigned)* sizeFuncionesConfigurables);

		ptr = funcionG;
		for (unsigned i = 0; i < numeroDeVertices; ++i) {
			valorG = grafo->getEtiquetaVertice(i);
			memcpy(ptr, &valorG, sizeof (unsigned));
			ptr += sizeof (unsigned);
		}
		salida->write(header, tamanioHeader);
		salida->write(funcionG, tamanioInfoTabla);
		delete[] header;
		delete[] funcionG;
		salida->close();
		delete(salida);
}

bool GeneradorHashingPerfecto::hidratar() {
	std::stringstream path;
	path << "../archivos/salida/hash.bin";
	std::string pathString = path.str();

	std::ifstream *entrada = new std::ifstream(pathString.c_str(), std::ios::in | std::ios::binary);
	unsigned *funciones;
	unsigned tamanioFunciones = 0;
	unsigned maxLongitudString = 0;
	unsigned *bufferValoresG;

	if (entrada->is_open()) {
		entrada->read((char*)&numeroDeVertices, sizeof(unsigned));
		entrada->read((char*)&numeroDeAristas, sizeof(unsigned));
		entrada->read((char*)&maxLongitudString, sizeof(unsigned));
		funcionUno->clear();
		funcionDos->clear();
		tamanioFunciones =  NUMERO_DE_FUNCIONES *maxLongitudString;
		sizeFuncionesConfigurables = maxLongitudString;
		funciones = new unsigned[tamanioFunciones];

		entrada->read((char *) funciones, sizeof (unsigned)*tamanioFunciones);
		for (unsigned i = 0; i < maxLongitudString; ++i) {
			funcionUno->push_back(funciones[i]);
		}
		for (unsigned i = maxLongitudString; i < tamanioFunciones; ++i) {
			funcionDos->push_back(funciones[i]);
		}
		bufferValoresG = new unsigned[numeroDeVertices];
		entrada->read((char *) bufferValoresG, sizeof (unsigned)*numeroDeVertices);
	}
	else {
		return false;
	}
	if (grafo) {
		delete(grafo);
		grafo = NULL;

	}
	grafo = new Grafo(numeroDeVertices, numeroDeAristas);
	std::cout << std::endl;
	unsigned valorG = 0;
	for (unsigned i = 0; i < numeroDeVertices; ++i) {
		valorG = bufferValoresG[i];
		grafo->setEtiquetasParaVertice(i,valorG);
	}


	delete[] funciones;
	delete[] bufferValoresG;
	entrada->close();
	delete(entrada);
	return true;
}

inline std::pair<unsigned, unsigned> GeneradorHashingPerfecto::aplicarFunciones(const std::string& palabra) {
	std::pair <unsigned, unsigned> funcionesEvaluadas;
	unsigned acumuladoFuncionUno = 0;
	unsigned acumuladoFuncionDos = 0;
	unsigned moduloFuncionUno = 0;
	unsigned moduloFuncionDos = 0;

	for (unsigned int i = 0; i < palabra.size() && i < sizeFuncionesConfigurables; ++i) {
		acumuladoFuncionUno += funcionUno->at(i) * palabra.at(i);
		acumuladoFuncionDos += funcionDos->at(i) * palabra.at(i);
	}
	moduloFuncionUno = (numeroDeVertices >0) ? acumuladoFuncionUno % numeroDeVertices: 0;
	moduloFuncionDos = (numeroDeVertices >0) ? acumuladoFuncionDos % numeroDeVertices: 0;

	funcionesEvaluadas.first = moduloFuncionUno;
	funcionesEvaluadas.second = moduloFuncionDos;
	return funcionesEvaluadas;
}

void GeneradorHashingPerfecto::configurarFunciones() {
	funcionUno->clear();
	funcionDos->clear();
	if (!hidratarGrafo) {
		for (unsigned i = 0; i < sizeFuncionesConfigurables; ++i) {
			funcionUno->push_back(generadorRandom->generarRandom());
			funcionDos->push_back(generadorRandom->generarRandom());
		}
	}
}

bool GeneradorHashingPerfecto::generarGrafo() {
	if (grafo) {
		delete(grafo);
		grafo = NULL;
	}

	grafo = new Grafo(numeroDeVertices, numeroDeAristas);
	grafo->crearVertices();

	std::pair<unsigned, unsigned> par;

	bool grafoCiclico = false;
	for (unsigned i = 0; i < numeroDeAristas; ++i) {
		par = aplicarFunciones(*palabras.at(i));
		grafo->dirigirNodo(par.first, par.second);
	}
	grafoCiclico = grafo->esCiclico();
	return !grafoCiclico;
}

void GeneradorHashingPerfecto::imprimirFunciones() {
		std::cout << "F1" << std::endl;
		for (unsigned i = 0; i < sizeFuncionesConfigurables; ++i) {
			std::cout << funcionUno->at(i);
		}

		std::cout  << std::endl;
		std::cout << "F2" << std::endl;
		for (unsigned i = 0; i < sizeFuncionesConfigurables; ++i) {
				std::cout << funcionDos->at(i);
		}
		std::cout  << std::endl;

}
void GeneradorHashingPerfecto::imprimirTablaHash() {


	std::cout << "H1(x)   " << "H2(x)" << std::endl;

	for (unsigned i = 0; i < numeroDeAristas; ++i) {
		std::pair<unsigned, unsigned> par = aplicarFunciones(*palabras.at(i));
		std::cout << par.first << "       "<<par.second << std::endl;
	}

}
inline unsigned GeneradorHashingPerfecto::obtenerIndex(const std::string& palabra) {
	unsigned index = 0;
	std::pair <unsigned, unsigned> par = aplicarFunciones(palabra);

	unsigned gDeFUno = grafo->getEtiquetaVertice(par.first);
	unsigned gDeFDos = grafo->getEtiquetaVertice(par.second);

	index = (gDeFUno + gDeFDos);

	return (numeroDeAristas>0)? (index% numeroDeAristas): index;
}
